/*
   (C) 2005 - Michael Ocean - Boston University

  This work is a part of the larger snBench project.  Information
  can be found at: http://csr.bu.edu/snbench

  This program is free software; you can redistribute it and/or
  modify it under the terms of Version 2 of the GNU General Public 
  License.
  
  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  General Public License for more details.
  
  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free
  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

package sxe;

import http.Status;

import java.io.IOException;
import java.net.ConnectException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;

import sd.ManagedSXE;
import snbenchlib.Log;
import snbenchlib.Trace;
import snobject.snCommand;
import snobject.snManagedSXE;
import snobject.snObject;
import sxe.sensors.GenericSensor;

/*
 * This class takes care of monitoring SxeExecution and communication with the
 * RM
 */
/*
 * The SXEMonitor will: - Perform admission control - Profile the function
 * execution - Update the RM as to the current XE state.
 */

public class SxeMonitor extends Thread {

	static private boolean bRun; /* indicates if the monitor is supposed to continue running */
	static private boolean bDone; /* indicates the monitor has completed running */

	static private ManagedSXE mSxe; 	/* the SXE that is being monitored */
	static private snManagedSXE snSXE; /* the serialized version of the above (to be posted) */

	private URI uriSD; 		/* the URI at which the SD can be reached */

	static private SxeMonitor instance = null; /* enables the singleton */
	
	/**
	 * making private ensures singleton style access
	 */
	private SxeMonitor(){}
	
	public static void create(String id, 
			URI self, 
			URI serviceDispatcher, 
			int default_resource_share, 
			ArrayList<GenericSensor> sensors) 
	{
		mSxe = new ManagedSXE(id,self,default_resource_share,sensors);
		instance = new SxeMonitor(serviceDispatcher);
	}
	
	/**
	 * Prints the SXE Info to the Log file for Log.server
	 */
	public static void PrintSXEInfo(){ 
		Log.server.log("====SXE Serialized info (for heartbeat)====");
		Log.server.log(mSxe.getXML());
	}

	private SxeMonitor(URI serviceDispatcher) {
		bRun = false;
		bDone = false;

		snSXE = new snManagedSXE(mSxe.getXML());

		if(serviceDispatcher!=null)
			try {
				uriSD = new URI(serviceDispatcher.toString() + "/snbench/rm/sxe/");
			} catch (URISyntaxException e) {
				/* this *really* shouldn't happen */
				Log.misc.printStackTrace(e);
			}
	}

	public static void decrAvailResources(int resources) {
		synchronized(instance){
			mSxe.decrAvailResources(resources);
			snSXE = new snManagedSXE(mSxe.getXML());
		}
	}

	public static void incrAvailableResources(int resources) {
		synchronized(instance){
			mSxe.incrAvailResources(resources);
			snSXE = new snManagedSXE(mSxe.getXML());
		}
	}
	
	public static int getAvailableResources() {
		synchronized(instance){
			return mSxe.getAvailResources();
		}
	}
	
	public static String getXML() {
		synchronized(instance){
			return mSxe.getXML();
		}
	}
	
	public snObject GetSnObject(){
		synchronized(instance){
			return snSXE;
		}
	}
	
	public boolean isDone() { return bDone; }
	public void setDone(boolean _bdone) {
		bDone = _bdone;
	}
	public synchronized boolean isRun() { return bRun; }
	public synchronized void setRun(boolean _bRun) {
		bRun = _bRun;
	}
	
	static public boolean reportEvaluationComplete(String sGraphName){
		return instance.tellSSDEvaluationComplete(sGraphName);
	}
	
	/* this method should tell the SSD that the evaluation of the
	 * STEP graph named sGraphName has completed evaluation.  The
	 * SSD will/should in turn tell us to mark the graph for 
	 * deletion.
	 */
	public boolean tellSSDEvaluationComplete(String sGraphName){
		if(uriSD==null)
			return true;
		Log.misc.log("SXEMonitor:Attempting to POST 'done' for program "+sGraphName +" to SD...", Trace.DEBUG);
		snObject sno = null;
		try {
			sno = http.Client.POST(uriSD, snCommand.EXDONE(sGraphName));
			Log.misc.log("SXEMonitor:Response: " + sno.getXml(), Trace.DEBUG);
			return true;
		} catch (ConnectException ce){
			Log.misc.log("SXEMonitor:Failed to connect to SSD at:" + uriSD);
		} catch (IOException e) {
			Log.misc.log("SXEMonitor:Failed to connect to SSD at:" + uriSD);
			Log.misc.printStackTrace(e);
		}		
		catch (Status e) {
			Log.misc.log("SXEMonitor:Failed to connect to SSD at:" + uriSD);
			Log.misc.printStackTrace(e);
		}
		return false;
	}
	
	private boolean contactSD() {
		snObject sno = null;
		Log.misc.log("SXEMonitor:Attempting to POST state to SD...", Trace.VERBOSE);
		
		try {
			sno = http.Client.POST(uriSD, GetSnObject());
			Log.misc.log("SXEMonitor:Response: " + sno.getXml() + ".  Free=" + mSxe.getAvailResources(), Trace.DEBUG);
			return true;
		} catch (ConnectException ce){
			Log.misc.log("SXEMonitor:Failed to connect to SSD at:" + uriSD);
		} catch (IOException e) {
			Log.misc.log("SXEMonitor:Failed to connect to SSD at:" + uriSD);
			Log.misc.printStackTrace(e);
		}		
		catch (Status e) {
			Log.misc.log("SXEMonitor:Failed to connect to SSD at:" + uriSD);
			Log.misc.printStackTrace(e);
		}
		return false;
	}

	static public void Start(){
		instance.start();
	}
	
	static public void Shutdown(){
		boolean bPrintOnce = false;
		
		instance.setRun(false);
		instance.notifyAll();
		
		while (!instance.isDone()) {
			if(!bPrintOnce){
				Log.server.log("..still waiting on SXE Monitor");
				bPrintOnce = true;
			}
		}	
	}
	
	public void run() {
		Log.misc.log("Starting SXEMonitor", Trace.DEBUG);

		setRun(true);
		int iErrors = 0;

		/*
		 * This is useful if you want to "fake" a post to a server try{
		 * snSXE.webWrite(new FileOutputStream(new File("sxe_info.xml"))); }
		 * catch(Exception e) { e.printStackTrace(); }
		 */

		while (isRun()) {
			if (!contactSD())
				iErrors++;
			else
				iErrors = 0;

			if (iErrors > 2) {
				Log.misc.log("SXEMonitor:Failed to connect to SSD at:" + uriSD
						+ " 3 times.  " +
						"SD contact thread disabled.");
				setRun(false);
				break;
			}

			/* Wait until next attempted contact
			 * we'll sleep for 1 minute for debugging,
			 * in real life, we'll contact much less
			 * frequently
			 */
			try {
				sleep(6000);
				//wait(6000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		/* if still busy, do one more pass */

		/* notify the SD, wait for ack */
		Log.misc.log("SXEMonitor shut down");
		setDone(true);
	}



}
